/*
  The MIT License
  
  Copyright (c) 2009 smath project
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/
package pl.smath.expression.utils;

import java.util.Comparator;

import pl.smath.expression.model.ExponentiationTerm;
import pl.smath.expression.model.NumberTerm;
import pl.smath.expression.model.ProductTerm;
import pl.smath.expression.model.Term;
import pl.smath.expression.model.VariableTerm;

public class SortSumComparator implements Comparator<Term> {

	
    public int compare(Term t1, Term t2) {
 
       	Integer val1 = 0;
		Integer val2 = 0;
    	
		boolean oneType1 = oneTypeOfSymbol(t1);
		boolean oneType2 = oneTypeOfSymbol(t2);

		Integer sNum1 = getSymbolsNumber(t1);
		Integer sNum2 = getSymbolsNumber(t2);

		Integer totalNum1 = getTotalNumber(t1);
		Integer totalNum2 = getTotalNumber(t2);
		
		Integer expNum1 = getExpNumber(t1);
		Integer expNum2 = getExpNumber(t2);

		//Integer exp1 = getLargestExp(t1);
		//Integer exp2 = getLargestExp(t2);
		
		Integer topVal1 = getTopSymbolValue(t1);
		Integer topVal2 = getTopSymbolValue(t2);
		
		if (topVal1 != topVal2)
			return topVal2.compareTo(topVal1);
		
		val1 += calculateValue(t1);
		val1 += 100 * sNum1;
		val1 += 200 * expNum1;
		val1 += totalNum1;
		if (oneType1)
			val1 += 1000000;

		val2 += calculateValue(t2);
		val2 += 100 * sNum2;
		val2 += 200 * expNum2;
		val2 += totalNum2;
		if (oneType2)
			val2 += 1000000;

        return val2.compareTo(val1);
    }
    
    private boolean oneTypeOfSymbol(Term term){
    	if (term instanceof ProductTerm){
        	VariableTerm prev = null;
        	int count = 0;
        	boolean hasExp = false;

        	for(Term t : ((ProductTerm) term).getTerms()){
        		
    			if (t instanceof VariableTerm){
    				if (prev != null && !t.isAbsTheSame(prev))
    					return false;
    				prev = (VariableTerm) t;
    				count++;
    			}
    			else if (t instanceof ExponentiationTerm){
    				if (((ExponentiationTerm) t).getBase() instanceof VariableTerm)
    				{
	    				if (prev != null && !t.isAbsTheSame(prev))
	    					return false;
	    				prev = (VariableTerm) ((ExponentiationTerm) t).getBase();
	    				count++;
	    				hasExp = true;
    				}
    			}
        	}
        	if (count > 1 || hasExp) return true;
    	}
    	else if (term instanceof ExponentiationTerm){
			if (((ExponentiationTerm) term).getBase() instanceof VariableTerm)
				return true;
    	}
   		return false;
    }
    
    private int getTopSymbolValue(Term term){
    	int val = 0;
    	if (term instanceof ProductTerm){
    		for(Term t : ((ProductTerm) term).getTerms()){
    			if (t instanceof VariableTerm){
    				int v = calculateSymbolValue((VariableTerm) t);
    				if (v > val)
    					val = v;
    			} else if (t instanceof ExponentiationTerm){
    				if (((ExponentiationTerm) t).getBase() instanceof VariableTerm){
    					int v = calculateSymbolValue((VariableTerm) ((ExponentiationTerm) t).getBase());
        				if (v > val)
        					val = v;
    				}
    			}
        	}
    	}
    	else if (term instanceof ExponentiationTerm){
			if (((ExponentiationTerm) term).getBase() instanceof VariableTerm){
				int v = calculateSymbolValue((VariableTerm) ((ExponentiationTerm) term).getBase());
				if (v > val)
					val = v;
			}
		}
    	else if (term instanceof VariableTerm)
    		return calculateSymbolValue((VariableTerm) term);
   		
    	return val;
    }
    
//    private int getLargestExp(Term term){
//    	int val = 0;
//    	if (term instanceof ProductTerm){
//    		Iterator<Term> e = ((ProductTerm) term).getTerms();
//        	while(e.hasNext()){
//    			Term t = e.next();
//    			if (t instanceof ExponentiationTerm){
//    				Term exponent = ((ExponentiationTerm) t).getExponent();
//    				if (exponent instanceof NumberTerm){
//    					int expVal = (int)((NumberTerm) exponent).getValue();
//    					if (expVal > val)
//    						val = expVal;
//    				}
//    			}
//        	}
//    	}
//    	else if (term instanceof ExponentiationTerm){
//			Term exponent = ((ExponentiationTerm) term).getExponent();
//			if (exponent instanceof NumberTerm){
//				int expVal = (int)((NumberTerm) exponent).getValue();
//				if (expVal > val)
//					val = expVal;
//			}
//		}
//   		return val;
//    }
    
    private int getTotalNumber(Term term){
    	if (term instanceof ProductTerm){
    		return ((ProductTerm) term).getTerms().size();
    	}
    	return 1;
    }
    
    private int getSymbolsNumber(Term term){
    	if (term instanceof ProductTerm){
    		int count = 0;
    		for(Term t : ((ProductTerm) term).getTerms()){
    			if (t instanceof VariableTerm)
    				count++;
        	}
    		return count;
    	}
    	else if (term instanceof VariableTerm)
    		return 1;
    	
    	return 0;
    }
    
    private int getExpNumber(Term term){
    	if (term instanceof ProductTerm){
    		int count = 0;
    		for(Term t : ((ProductTerm) term).getTerms()){
    			if (t instanceof ExponentiationTerm)
    				count++;
        	}
    		return count;
    	}
    	else if (term instanceof ExponentiationTerm)
    		return 1;
    	
    	return 0;
    }
    
    private int calculateValue(Term term){
		if (term instanceof ExponentiationTerm)
			return calculateExponentialValue((ExponentiationTerm) term);
		else if (term instanceof ProductTerm)
			return calculateProductValue((ProductTerm) term);
		else if (term instanceof VariableTerm)
			return calculateSymbolValue((VariableTerm) term);

		return 0;
    }
    
    private int calculateExponentialValue(ExponentiationTerm term){
    	int val = 0;
    	Term exponent = term.getExponent();
		Term base = term.getBase();
		
		int baseVal = 0;
		if (base instanceof VariableTerm){
			baseVal = calculateSymbolValue((VariableTerm) base); 
		}

		if (exponent instanceof NumberTerm){
			int expVal = (int)((NumberTerm) exponent).getValue();
			val += baseVal*expVal;
		}
		else
			val += 1;
			
    	return val;
    }
    
    private int calculateProductValue(ProductTerm term){
    	
    	int val = 0;
    	
    	for(Term t : ((ProductTerm) term).getTerms()){
			if (t instanceof ExponentiationTerm)
				val += calculateExponentialValue((ExponentiationTerm) t);
			else if (t instanceof VariableTerm){
				val += calculateSymbolValue((VariableTerm) t);
			}
		}
		
		return val;
    }
    
    private int calculateSymbolValue(VariableTerm term){
    	
    	Character symbol = term.getText().toLowerCase().charAt(0);
    	return 123 - (int)symbol;
    }
}
